<template>
  <div>
    <!-- on-preview	点击文件列表中已上传的文件时的钩子 -->
    <!-- http-request	覆盖默认的上传行为，可以自定义上传的实现 -->
    <!-- limit	最大允许上传个数 -->
    <!-- before-upload	上传文件之前的钩子，参数为上传的文件，若返回 false 或者返回 Promise 且被 reject，则停止上传。 -->
    <!-- accept	接受上传的文件类型（thumbnail-mode 模式下此参数无效） -->
    <!-- multiple	是否支持多选文件 -->
    <!-- on-change	文件状态改变时的钩子，添加文件、上传成功和上传失败时都会被调用 -->
    <!-- on-remove	文件列表移除文件时的钩子 -->
    <!-- file-list	上传的文件列表, 例如: [{name: 'food.jpg', url: 'https://xxx.cdn.com/xxx.jpg'}] -->
    <!-- on-exceed	文件超出个数限制时的钩子 -->
    <!-- auto-upload	是否在选取文件后立即进行上传 -->
    <!-- action	必选参数，上传的地址  例如  action="https://jsonplaceholder.typicode.com/posts/"-->
    <el-upload
      ref="upload"
      :drag="drag"
      :accept="accept"
      :before-upload="beforeUpload"
      multiple
      :auto-upload="true"
      :http-request="checkedFile"
      :before-remove="removeFile"
      :on-remove="handleRemove"
      :limit="limit"
      action=""
      :file-list="fileList"
    >
      <template v-if="!drag">
        <slot name="add">
          <div>
            <img :class="[imgClass, 'disb']" :src="require('@/assets/img/add_card.png')" />
            <div slot="tip" class="el-upload__tip">只能上传{{ accept }}文件，且不超过{{ maxSizeAll }}M</div>
          </div>
        </slot>
<!--        <el-button size="small" type="primary">点击上传</el-button>-->
<!--        <div slot="tip" class="el-upload__tip">只能上传{{ accept }}文件，且不超过{{ maxSizeAll }}M</div>-->
      </template>
      <template v-if="drag">
        <i class="el-icon-upload"></i>
        <div class="el-upload__text">将文件拖到此处，或<em>点击上传</em></div>
        <div class="el-upload__tip" slot="tip">只能上传{{ accept }}文件，且不超过{{ maxSizeAll }}M</div>
      </template>
    </el-upload>
    <el-progress type="circle" :percentage="progress" class="progress" v-if="showProgress"></el-progress>
  </div>
</template>
<script>
//切片上传
import axios from "axios";
import SparkMD5 from "spark-md5";
export default {
  props: {
    // 是否启用拖拽上传
    drag: {
      type: Boolean,
      default: false
    },
    // 接受上传的文件类型
    accept: {
      type: String,
      default: '.mp4,.m2v,.mkv'
    },
    // 最大上传兆数
    maxSizeAll: {
      type: Number,
      default: 50,
    },
    imgClass: {
      type: String,
      default: 'img36 disb',
    },
    limit: {
      type: [Number, String],
      default: 1,
    },
  },
  data() {
    return {
      maxSize: 50 * 1024 * 1024, // 上传最大文件限制  最小单位是b
      // multiUploadSize: 100 * 1024 * 1024, // 大于这个大小的文件使用分块上传(后端可以支持断点续传)  100mb
      // eachSize: 100 * 1024 * 1024, // 每块文件大小   100mb
      multiUploadSize: 1 * 1024 * 1024, // 大于这个大小的文件使用分块上传(后端可以支持断点续传)  1mb
      eachSize: 1 * 1024 * 1024, // 每块文件大小   1mb
      requestCancelQueue: [], // 请求方法队列（调用取消上传
      url: "/tools/upload_test/",
      //上传进度
      progress: 0,
      showProgress: false,
      // 每上传一块的进度
      eachProgress: 0,
      // 总共有多少块。断点续传使用
      chunksKeep:0,
      // 切割后的文件数组
      fileChunksKeep:[],
      // 这个文件，断点续传
      fileKeep:null,

      //上传列表
      urlList: [],
      fileList: [],
      allowRequest: true,
    };
  },
  mounted() {

  },
  watch: {
    fileList: {
      handler() {
        // console.log('000')
        this.$emit('fileList', this.fileList)
      },
      deep: true,
      immediate: true
    }
  },
  methods: {
    async checkedFile(options) {
      console.log(options);
      const  vm = this
      // let Flag =
      //   this.fileList &&
      //   this.fileList.some(item => {
      //     if (options.file.name === item.name) {
      //       this.$message.error('文件名称重复')
      //       return true
      //     }
      //   })
      // if (!Flag) {
      //   this.fileList.push(options.file)
      //   vm.allowRequest=true
      // } else {
      //   this.fileList = this.fileList.slice(0, this.fileList.length)
      //   vm.allowRequest=false
      //   vm.closeFullScreen(vm.openFullScreen());
      // }
      // // console.log('vm.allowRequest', vm.allowRequest)
      // if(!vm.allowRequest) return

      const {
        maxSize,
        multiUploadSize,
        getSize,
        splitUpload,
        singleUpload
      } = this; // 解构赋值
      const { file, onProgress, onSuccess, onError } = options; // 解构赋值
      // if (file.size > maxSize) {
      //   return this.$message({
      //     message: `您选择的文件大于${getSize(maxSize)}`,
      //     type: "error"
      //   });
      // }
      if(file.size/(1024*1024)>vm.maxSizeAll){
        //删除单个文件
        vm.uploadFilesClear(options.file.name)
        return this.$message({
          message: `您选择的文件大于${getSize(vm.maxSizeAll)}M`,
          // message: '上传文件应小于'+vm.maxSizeAll+'M',
          type: "error"
        });
      }
      console.log('multiUploadSize file.size', file.size, multiUploadSize)
      this.fileKeep = file
      const uploadFunc =
        file.size > multiUploadSize ? splitUpload : singleUpload; // 选择上传方式
      try {
        await uploadFunc(file, onProgress);
        this.$message({
          message: "上传成功",
          type: "success"
        });
        this.showProgress = false;
        this.progress = 0;
        onSuccess();
      } catch (e) {
        console.error(e);
        this.$message({
          message: e.message,
          type: "error"
        });
        this.showProgress = false;
        this.progress = 0;
        onError();
      }
      const prom = new Promise((resolve, reject) => {}); // 上传后返回一个promise
      prom.abort = () => {};
      return prom;
    },
    // 格式化文件大小显示文字
    getSize(size) {
      return size > 1024
        ? size / 1024 > 1024
          ? size / (1024 * 1024) > 1024
            ? (size / (1024 * 1024 * 1024)).toFixed(2) + "GB"
            : (size / (1024 * 1024)).toFixed(2) + "MB"
          : (size / 1024).toFixed(2) + "KB"
        : size.toFixed(2) + "B";
    },
    // 单文件直接上传
    async singleUpload(file, onProgress) {
      await this.postFile(
        { file, uid: file.uid, fileName: file.fileName ,chunk:0, chunks: 1},
        onProgress
      );
      // const spark = new SparkMD5.ArrayBuffer();
      // spark.append(file);
      // const md5 = spark.end();
      // console.log(md5);
      // const isValidate = await this.validateFile({
      //   fileName: file.name,
      //   uid: file.uid,
      //   md5:md5,
      //   chunks:1
      // });
    },
    // 大文件分块上传
    splitUpload(file, onProgress) {
      console.log('file11')
      console.log(file)
      return new Promise(async (resolve, reject) => {
        try {
          const { eachSize } = this;
          const chunks = Math.ceil(file.size / eachSize);
          this.chunksKeep = chunks
          const fileChunks = await this.splitFile(file, eachSize, chunks);
          this.fileChunksKeep = fileChunks
          console.log('fileChunks,文件数组切割后')
          console.log(fileChunks)
          //判断每上传一个文件，进度条涨多少,保留两位小数

          this.eachProgress = parseInt(Math.floor(100 / chunks * 100) / 100);

          this.showProgress = true;
          let currentChunk = 0;
          for (let i = 0; i < fileChunks.length; i++) {
            // 服务端检测已经上传到第currentChunk块了，那就直接跳到第currentChunk块，实现断点续传
            console.log(currentChunk, i);
            // 此时需要判断进度条

            if (Number(currentChunk) === i) {
              // 每块上传完后则返回需要提交的下一块的index
              await this.postFile(
                {
                  chunked: true,
                  chunk: i,
                  chunks: chunks,
                  eachSize,
                  fileName: file.name,
                  fullSize: file.size,
                  uid: file.uid,
                  file: fileChunks[i]
                },
                onProgress
              );
              currentChunk++

              // 上传完一块后，进度条增加
              this.progress += this.eachProgress;
              // 不能超过100
              this.progress = this.progress > 100 ? 100 : this.progress;
            }
          }
          const spark = new SparkMD5.ArrayBuffer();
          spark.append(file);
          const md5 = spark.end();
          console.log(md5);
          const isValidate = await this.validateFile({
            chunks: fileChunks.length,
            // chunk: fileChunks.length,
            fileName: file.name,
            uid: file.uid,
            md5:md5,
            // task_id:file.uid
          });
          // if (!isValidate) {
          //   throw new Error("文件校验异常");
          // }
          resolve();
        } catch (e) {
          reject(e);
        }
      });
    },
    againSplitUpload(file, array) {
      console.log('file,array')
      console.log(file)
      console.log(array)
      return new Promise(async (resolve, reject) => {
        try {
          const { eachSize , fileKeep } = this;
          const chunks = this.chunksKeep
          const fileChunks = this.fileChunksKeep
          this.showProgress = true;
          // let currentChunk = 0;
          for (let i = 0; i < array.length; i++) {
            // 服务端检测已经上传到第currentChunk块了，那就直接跳到第currentChunk块，实现断点续传
            // console.log(currentChunk, i);
            // 此时需要判断进度条
            // 每块上传完后则返回需要提交的下一块的index
            await this.postFile(
              {
                chunked: true,
                chunk: array[i],
                chunks: chunks,
                fileName: file.fileName,
                fullSize: fileKeep.size,
                uid: file.uid,
                file: fileChunks[array[i]]
              },
            );
            // currentChunk++

            // 上传完一块后，进度条增加
            // this.progress += this.eachProgress;
            // 不能超过100
            this.progress = this.progress > 100 ? 100 : this.progress;
          }
          const spark = new SparkMD5.ArrayBuffer();
          spark.append(fileKeep);
          const md5 = spark.end();
          console.log(md5);
          const isValidate = await this.validateFile({
            chunks: fileChunks.length,
            // chunk: fileChunks.length,
            fileName: file.fileName,
            uid: file.uid,
            md5:md5,
            // task_id:file.uid
          });
          // if (!isValidate) {
          //   throw new Error("文件校验异常");
          // }
          resolve();
        } catch (e) {
          reject(e);
        }
      });
    },
    // 文件分块,利用Array.prototype.slice方法
    splitFile(file, eachSize, chunks) {
      return new Promise((resolve, reject) => {
        try {
          setTimeout(() => {
            const fileChunk = [];
            for (let chunk = 0; chunks > 0; chunks--) {
              fileChunk.push(file.slice(chunk, chunk + eachSize));
              chunk += eachSize;
            }
            resolve(fileChunk);
          }, 0);
        } catch (e) {
          console.error(e);
          reject(new Error("文件切块发生错误"));
        }
      });
    },
    removeFile(file) {
      this.requestCancelQueue[file.uid]();
      delete this.requestCancelQueue[file.uid];
      return true;
    },
    // 提交文件方法,将参数转换为FormData, 然后通过axios发起请求
    postFile(param, onProgress) {
      const vm = this
      console.log('postFile', param);
      let formData = new FormData();
      // for (let p in param) {
      // formData.append(p, param[p]);
      // }
      // param.fullSize=param.fullSize*1024
      formData.append('file', param.file)  //  改了
      formData.append('uid',param.uid)
      formData.append('chunk',param.chunk)
      formData.append('chunks',param.chunks)
      formData.append('fileName',param.fileName)
      formData.append('fullSize',param.fullSize)
      const { requestCancelQueue } = this;
      const config = {
        cancelToken: new axios.CancelToken(function executor(cancel) {
          if (requestCancelQueue[param.uid]) {
            requestCancelQueue[param.uid]();
            delete requestCancelQueue[param.uid];
          }
          requestCancelQueue[param.uid] = cancel;
        }),
        onUploadProgress: e => {
          if (param.chunked) {
            e.percent = Number(
              (
                ((param.chunk * (param.eachSize - 1) + e.loaded) /
                  param.fullSize) *
                100
              ).toFixed(2)
            );
          } else {
            e.percent = Number(((e.loaded / e.total) * 100).toFixed(2));
          }
          onProgress(e);
        }
      };
      return this.upload.uploadFile(formData,'api/upload/uploadSlice').then(res => {
          // console.log("res",res)
          const { code, data, msg }= res
          if (code == 0) {
            this.closeFullScreen(this.openFullScreen());
            // 单次上传
            if(param.chunks == 1){
              this.$message('上传成功');
              if(data){
                //上传列表
                vm.urlList = vm.urlList.concat([{
                  ...data,
                  fileUrl: data.f_url
                }])
                vm.fileList=vm.urlList;
                vm.$emit('complete', vm.urlList);
              }else {
                if(vm.limit == 1){
                  vm.clearFiles();
                }
              }
            }
          } else {
            this.closeFullScreen(this.openFullScreen());
            this.$message.error(msg||'上传失败');
          }
      })
    },
    // 文件校验方法
    validateFile(file) {
      const vm = this
      console.log('file', file)
      return vm.apiUrl.breakpointUploadSuccess(file,"POST").then(res => {
        const { code, data } = res
        if (code == 0) {
          // vm.againSplitUpload(file, data.error_file)
          if(data){
            //上传列表
            vm.urlList = vm.urlList.concat([{
              ...data,
              fileUrl: data.f_url
            }])
            vm.fileList=vm.urlList;
            vm.$emit('complete', vm.urlList);
          }else {
            if(vm.limit == 1) {
              vm.clearFiles();
            }
          }
          return true
        }
      })
    },
    uploadFilesClear(fileName){
      //删除单个文件
      const { uploadFiles } = this.$refs.upload
      if (uploadFiles && uploadFiles.length && fileName) {
        uploadFiles.splice(uploadFiles.findIndex(item => item.name === fileName), 1)
      }
    },
    //校验删除清空等
    //校验
    beforeUpload(file) {
      const vm = this
      vm.openFullScreen()
      // console.log('检查大小 格式',file)
      // 图片大小限制
      if(file.size/(1024*1024)>vm.maxSizeAll){
        vm.closeFullScreen(vm.openFullScreen());
        vm.showMessageMin({
          message: '上传文件应小于'+vm.maxSizeAll+'M',
          type: 'error'
        })
        vm.allowRequest = false
        if(vm.limit == 1) {
          vm.clearFiles();
        }
        //删除单个文件
        vm.uploadFilesClear(file.name)
        return;
      }
      //    上传类型判断
      const imgName = file.name;
      const idx = imgName.lastIndexOf(".");
      if (idx != -1) {
        let ext = imgName.substr(idx + 1).toUpperCase();
        ext = ext.toLowerCase();
        // console.log('includes',vm.accept.split(',').includes('.'+ext))
        // const allow = vm.accept.split(',').includes('.'+ext)
        const allow = vm.accept.replaceAll(' ','').split(',').includes('.'+ext)
        if (!allow) {
          vm.closeFullScreen(vm.openFullScreen());
          vm.showMessageMin({
            message: `只能上传${vm.accept}文件`,
            type: 'error'
          })
          vm.allowRequest = false
          if(vm.limit == 1) {
            vm.clearFiles();
          }
          return;
        }
      }
      vm.allowRequest = true
    },
    // 清空已上传的文件列表
    clearFiles(){
      const vm = this
      vm.$refs.upload.clearFiles();
      vm.clearFileList()
      vm.getUrlList(vm.fileList, '', true)
    },
    clearFileList() {
      const vm = this
      vm.fileList = []
      vm.urlList=vm.fileList;
    },
    // 获取选中列表
    getUrlList(arr, filename, clear=false){
      const vm = this
      if(!clear){
        vm.urlList = arr.filter(item=>item.filename != filename)
      } else {
        vm.urlList = []
      }
      // vm.$emit('getUrlList', vm.urlList)
    },
    // 文件移除
    handleRemove(file, fileList) {
      const vm = this
      // vm.uploadDelete(file)
      vm.urlList = vm.urlList.filter(item => {
        return item.uid != file.uid
      })
      vm.fileList=vm.urlList;
      vm.$emit('complete', vm.urlList);
    },
    uploadDelete(file){
      const vm = this
      const fileObj = this.urlList.filter(item => {
        return item.uid == file.uid
      })
      const f_url = fileObj[0].f_url
      vm.openFullScreen();
      vm.apiUrl.uploadDelete({
        file: f_url
      },"POST")
        .then(res => {
          vm.closeFullScreen(vm.openFullScreen());
          const { code,msg } = res;
          if (code != 0) {
            vm.showMessageMin({
              type: 'error',
              message: msg
            })
          } else {
            vm.showMessageMin({
              type: 'success',
              message: '文件已删除！'
            })
            this.fileList = this.fileList.filter(item => {
              return item.uid != file.uid
            })
            vm.getUrlList(vm.urlList, file.name)
          }
        })
    },

  }
};
</script>
<style lang="less" scoped>
.progress{
  /* 在当前页面居中 */
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  /* 宽度 */
}
</style>
